﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Imaging;
using System.Drawing;
using RMS.Utils.Dicom.DataStructure;
using System.IO;

namespace RMS.Utils.Dicom.Commen
{
    public class ImageData
    {
        #region [Properties]
        public int ImgWidth;
        public int ImgHeight;
        public int SamplesPerPixel;
        public int BitsStoredTag;
        public int HighBitTag;
        public int WindowsCentre;
        public int WindowsWidth;
        public int pixelRepresentation;
        public int BitsAllocated = 16;

        private Bitmap bmp;

        List<byte> pixels8;
        List<ushort> pixels16;
        List<byte> pixels24;

        byte[] lut8 = new byte[256];
        byte[] lut16 = new byte[65536];

        int winMax;
        int winMin;

        int min8 = Char.MinValue;
        int min16 = short.MinValue;
        #endregion

        #region [PublicMethod]
        public void ReadPixel()
        {
            winMax = Convert.ToInt32(WindowsCentre + 0.5 * WindowsWidth);
            winMin = winMax - WindowsWidth;
            bmp = new Bitmap(ImgWidth, ImgHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);

            #region [Image8]

            if (SamplesPerPixel == 1 && BitsAllocated == 8)
            {
                if (pixels8 != null)
                    pixels8.Clear();
                pixels8 = new List<byte>();
                int numPixels = ImgWidth * ImgHeight;
                byte[] buf = StreamFile.PixelData;
                for (int i = 0; i < numPixels; ++i)
                {
                    pixels8.Add((byte)(pixelRepresentation == 1 ? buf[i] : (buf[i] + min8)));
                }
                CreateImage8();
            }
            #endregion
            #region [Image16]

            else if (SamplesPerPixel == 1 && BitsAllocated == 16)
            {
                if (pixels16 != null)
                    pixels16.Clear();
                pixels16 = new List<ushort>();
                int numPixels = ImgWidth * ImgHeight;
                byte[] buf = StreamFile.PixelData;
                ushort unsignedS;
                ushort unsignedS1;
                int i, i1;
                byte b0, b1;
                byte[] signedData = new byte[2];

                for (i = 0; i < numPixels; ++i)
                {
                    i1 = i * 2;
                    b0 = buf[i1];
                    b1 = buf[i1 + 1];
                    unsignedS1 = Convert.ToUInt16((b1 << 8) + b0);
                    if (pixelRepresentation == 0) // Unsigned
                    {
                        unsignedS = unsignedS1;
                    }
                    else  // Pixel representation is 1, indicating a 2s complement image
                    {
                        signedData[0] = b0;
                        signedData[1] = b1;
                        short s8 = System.BitConverter.ToInt16(signedData, 0);
                        int s4 = s8 - min16; ;
                        unsignedS = (ushort)(s4);
                    }
                    pixels16.Add(unsignedS);
                }
                CreateImage16();
            }
            #endregion

            #region [Image24]   
            else if (SamplesPerPixel == 3 && BitsAllocated == 8)
            {
                if (pixels24 != null)
                    pixels24.Clear();
                pixels24 = new List<byte>();
                int numPixels = ImgWidth * ImgHeight;
                int numBytes = numPixels * SamplesPerPixel;
                byte[] buf = StreamFile.PixelData;
                for (int i = 0; i < numBytes; ++i)
                {
                    pixels24.Add(buf[i]);
                }
            }
            #endregion
            else
                throw new Exception("NoImageFound");
        }
        public void SaveAs(string path)
        {
            bmp.Save(path, ImageFormat.Jpeg);
        }
        #endregion

        #region [PrivetMethod]
        private void CreateImage8()
        {
            ComputeLookUpTable8();
            BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, ImgWidth, ImgHeight),
                System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);

            unsafe
            {
                int pixelSize = 3;
                int i, j, j1, i1;
                byte b;

                for (i = 0; i < bmd.Height; ++i)
                {
                    byte* row = (byte*)bmd.Scan0 + (i * bmd.Stride);
                    i1 = i * bmd.Width;

                    for (j = 0; j < bmd.Width; ++j)
                    {
                        b = lut8[pixels8[i * bmd.Width + j]];
                        j1 = j * pixelSize;
                        row[j1] = b;            // Red
                        row[j1 + 1] = b;        // Green
                        row[j1 + 2] = b;        // Blue
                    }
                }
            }
        }
        private void ComputeLookUpTable8()
        {
            if (winMax == 0)
                winMax = 255;

            int range = winMax - winMin;
            if (range < 1) range = 1;
            double factor = 255.0 / range;

            for (int i = 0; i < 256; ++i)
            {
                if (i <= winMin)
                    lut8[i] = 0;
                else if (i >= winMax)
                    lut8[i] = 255;
                else
                {
                    lut8[i] = (byte)((i - winMin) * factor);
                }
            }
        }
        private void CreateImage16()
        {
            ComputeLookUpTable16();
            BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, ImgWidth, ImgHeight),
               System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);

            unsafe
            {
                int pixelSize = 3;
                int i, j, j1, i1;
                byte b;

                for (i = 0; i < bmd.Height; ++i)
                {
                    byte* row = (byte*)bmd.Scan0 + (i * bmd.Stride);
                    i1 = i * bmd.Width;

                    for (j = 0; j < bmd.Width; ++j)
                    {
                        b = lut16[pixels16[i * bmd.Width + j]];
                        j1 = j * pixelSize;
                        row[j1] = b;            // Red
                        row[j1 + 1] = b;        // Green
                        row[j1 + 2] = b;        // Blue
                    }
                }
            }
            bmp.UnlockBits(bmd);
        }
        private void ComputeLookUpTable16()
        {
            int range = winMax - winMin;
            if (range < 1) range = 1;
            double factor = 255.0 / range;
            int i;

            for (i = 0; i < 65536; ++i)
            {
                if (i <= winMin)
                    lut16[i] = 0;
                else if (i >= winMax)
                    lut16[i] = 255;
                else
                {
                    lut16[i] = (byte)((i - winMin) * factor);
                }
            }
        }
        private void CreateImage24()
        {
            ComputeLookUpTable8();
            BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, bmp.Width,
                bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
            unsafe
            {
                int numBytes = ImgWidth * ImgHeight * 3;
                int j;
                int i, i1;

                int width3 = bmd.Width * 3;

                for (i = 0; i < bmd.Height; ++i)
                {
                    byte* row = (byte*)bmd.Scan0 + (i * bmd.Stride);
                    i1 = i * bmd.Width * 3;

                    for (j = 0; j < width3; j += 3)
                    {
                        // Windows uses little-endian, so the RGB data is 
                        //  actually stored as BGR
                        row[j + 2] = lut8[pixels24[i1 + j]];     // Blue
                        row[j + 1] = lut8[pixels24[i1 + j + 1]]; // Green
                        row[j] = lut8[pixels24[i1 + j + 2]]; // Red
                    }
                }
            }
            bmp.UnlockBits(bmd);
        }
        #endregion

    }
}
